package servicebase

// this file deprecated

import (
	"bytes"
	"crypto/tls"
	"errors"
	proto "github.com/huin/mqtt"
	"github.com/jeffallen/mqtt"
	"jkgo/jk/jklog"
	"net"
	"os"
	"strconv"
	"time"
)

// Command transfer rule
// client -->  server
// imgproc --> server
// if client want send message to imgproc, give topic like /imgproc/imgdetect
// then imgproc will give response, each response is like {sendurl}/response

type IoTServer struct {
	Server *mqtt.Server
}

func NewIoTServer(addr string, port int) (*IoTServer, error) {
	cstr := addr + ":" + strconv.Itoa(port)
	l, err := net.Listen("tcp", cstr)
	if err != nil {
		jklog.L().Errorln("listen: ", err)
		return nil, err
	}
	svr := mqtt.NewServer(l)
	svr.Start()

	iot := &IoTServer{
		Server: svr,
	}
	return iot, nil
}

func NewIotServerTls(addr string, port int) (*IoTServer, error) {
	c, err := tls.LoadX509KeyPair("server.crt", "server.key")
	if err != nil {
		panic(err)
	}
	cert := []tls.Certificate{c}
	cfg := &tls.Config{
		Certificates: cert,
		NextProtos:   []string{"mqtt"},
	}
	l, err := tls.Listen("tcp", ":"+strconv.Itoa(port), cfg)
	if err != nil {
		jklog.L().Errorln("listen: ", err)
		return nil, err
	}
	svr := mqtt.NewServer(l)
	svr.Start()
	iot := &IoTServer{
		Server: svr,
	}
	return iot, nil
}

func (ss *IoTServer) Close() {
	ss.Server.Done <- struct{}{}
}

// Below are client inferface

type IoTClient struct {
	Client      *mqtt.ClientConn
	IsConnected bool
	Addr        string
	Port        int
	Id          string
	Url         string

	mh MsgHandle
}

func NewIoTClient(addr string, port int, id string) (*IoTClient, error) {
	return &IoTClient{
		Addr: addr,
		Port: port,
		Id:   id,
	}, nil
}

func (ss *IoTClient) Close() {
	ss.Client.Disconnect()
}

func (ss *IoTClient) SetMsgHandle(mh MsgHandle) {
	ss.mh = mh
}

func (ss *IoTClient) Pub(topic string, payload string) error {
	if !ss.IsConnected {
		return errors.New("client not connected")
	}

	ss.Client.Publish(&proto.Publish{
		Header:    proto.Header{Retain: true},
		TopicName: topic,
		Payload:   proto.BytesPayload([]byte(payload)),
	})

	return nil
}

// cycle to connect and send sub with /id if success for once
func (ss *IoTClient) Connect() {
	for {
		if !ss.IsConnected {
			conn, err := net.Dial("tcp", ss.Addr+":"+strconv.Itoa(ss.Port))
			if err != nil {
				jklog.L().Errorln(os.Stderr, "dial: ", err)
				time.Sleep(time.Second * 5)
				continue
			}
			ss.Client = mqtt.NewClientConn(conn)
			ss.Client.Dump = true
			ss.Client.ClientId = ss.Id

			user := ""
			pass := ""

			if err := ss.Client.Connect(user, pass); err != nil {
				jklog.L().Errorln("connect: %v\n", err)
				time.Sleep(time.Second * 5)
				continue
			}
			jklog.L().Infoln("Connected with client id", ss.Client.ClientId)

			ss.IsConnected = true
			ss.Sub("/" + ss.Id)
			ss.mh.IoTStatusReport("connected")
		}
		time.Sleep(time.Second * 1)
	}
}

func (ss *IoTClient) Sub(topic ...string) error {
	if !ss.IsConnected {
		return errors.New("client not connected")
	}
	jklog.L().Debugln("sub topic count ", len(topic))
	tq := make([]proto.TopicQos, len(topic))

	for i, u := range topic {
		tq[i].Topic = u
		tq[i].Qos = proto.QosAtMostOnce
	}
	ss.Client.Subscribe(tq)

	return nil
}

func (ss *IoTClient) Recv() {
	for {
		if !ss.IsConnected {
			time.Sleep(1 * time.Second)
			continue
		}
		for m := range ss.Client.Incoming {
			jklog.L().Debugln("iot recv message of topic ", m.TopicName)
			buf := bytes.NewBuffer([]byte{})
			m.Payload.WritePayload(buf)
			if ss.mh != nil {
				ss.mh.IoTDealMessage(m.TopicName, string(buf.Bytes()))
			}
		}
		time.Sleep(time.Millisecond * 50)
	}
}

func NewIoTClientStart(addr string, port int, id string, mh MsgHandle) (*IoTClient, error) {
	iot, err := NewIoTClient(addr, port, id)

	go iot.Connect()
	go iot.Recv()
	iot.SetMsgHandle(mh)
	return iot, err
}
